package com.pic.mgt.haishin.manager.business;

import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.springframework.dao.DataAccessException;
import org.springframework.dao.DuplicateKeyException;

import com.pic.mgt.business.operation.BusinessOperation;
import com.pic.mgt.business.operation.BusinessOperationException;
import com.pic.mgt.dao.BusinessListMgtDao;
import com.pic.mgt.dao.IBusinessListMgtDao;
import com.pic.mgt.haishin.HaishinBusinessUnitFactory;
import com.pic.mgt.log.IgxLog;
import com.pic.mgt.manager.business.BasicBusinessInfoBean;
import com.pic.mgt.manager.business.BusinessManagementOperationException;
import com.pic.mgt.manager.business.SimpleBusinessManager;
import com.pic.mgt.manager.business.SynchronizeBusinessException;
import com.pic.mgt.syushin.SyushinBusinessUnitFactory;

public final class SimpleHaishinBusinessManager extends SimpleBusinessManager
		implements HaishinBusinessManager {
	
	private BusinessOperation businessOperation;
	
	public SimpleHaishinBusinessManager(IBusinessListMgtDao businessListMgtDao,
		     							String syushinBatchTmpPathStr,
		     							int businessNameLen) throws BusinessManagementOperationException {
		super(businessListMgtDao, syushinBatchTmpPathStr, businessNameLen);
	}
	
	@Override
	protected void synchronizeAddBusiness(
			Map<String, Object> businessFolderNameMap,
			Map<String, BasicBusinessInfoBean> basicBusinessInfoBeanInDbMap)
			throws BusinessManagementOperationException {
		
		// 如果DB的業務名稱不存在於目錄中， 則必須新增。
		
		Set<String> businessNameInDbSet = basicBusinessInfoBeanInDbMap.keySet();
		for (String businessNameInDb : businessNameInDbSet) {
			BasicBusinessInfoBean basicBusinessInfoBeanInDb =
				  basicBusinessInfoBeanInDbMap.get(businessNameInDb);
			if (!businessFolderNameMap.containsKey(businessNameInDb)) {
				// DB的業務名稱不存在於目錄中，必須進行新增
				
				try {
					// throws DataAccessException, TerminalOperationException
					doSynchronizeAddBusinessAction(basicBusinessInfoBeanInDb);
					
					// 正確完成新增"後",通知 BusinessListener
					super.businessManagementListener.
							afterSynchronizeAddedBusinessNotify(basicBusinessInfoBeanInDb);
				} catch (SynchronizeBusinessException e2) {
					try {
						throw new BusinessManagementOperationException(
								"SUDA-230 Adding business '" + businessNameInDb
								+ "' folder failed.", e2);
					} catch (BusinessManagementOperationException e) {
						// ********************** Log **********************
						IgxLog.messageLog.error(this, e.getMessage());
						// ********************** Log **********************

						throw e;
					}
				}
			}
		}
		
	}
	
	private void doSynchronizeAddBusinessAction(
			BasicBusinessInfoBean toBeAddedBasicBusinessInfoBean)
			throws SynchronizeBusinessException {
		try {
			// throws DataAccessException, TerminalOperationException
			doAddBusiness(toBeAddedBasicBusinessInfoBean, false);
		} catch (DataAccessException e1) {
			throw new SynchronizeBusinessException("", e1);
		} catch (BusinessOperationException e2) {
			throw new SynchronizeBusinessException("", e2); 
		}
	}
	
	private void doAddBusiness(BasicBusinessInfoBean toBeAddedBasicBusinessInfoBean,
							   boolean isIncludeDbInsert)
							   throws DataAccessException, BusinessOperationException {

		/*
		 * (1) 新增業務至 DB (如果 isIncludeDbDelete 為 true)
		 * (2) 新增業務目錄
		 * (3) 從業務清單中新增業務
		 */

		String newBusinessName = toBeAddedBasicBusinessInfoBean.getBusinessName();

		synchronized (super.basicBusinessInfoBeanMap) {
			// (1) 新增業務至 DB
			if (isIncludeDbInsert) {
				try {
					insertBusinessToDB(toBeAddedBasicBusinessInfoBean);

					// ********************** Log **********************
					if (IgxLog.messageLog.isDebugMode()) {
						IgxLog.messageLog.debug(this,
								"Inserted business record '{}' to database.",
								toBeAddedBasicBusinessInfoBean.getBusinessName());
					}
					// ********************** Log **********************
				} catch (DuplicateKeyException e1) {
					// 代表資料庫內已經存在該業務的資料(寫Log後，忽略之，繼續往下做。)

					// ********************** Log **********************
					if (IgxLog.messageDetailsLog.isWarnMode()) {
						IgxLog.messageDetailsLog.warn(this, e1.getMessage());
					}
					// ********************** Log **********************

					// ********************** Log **********************
					if (IgxLog.messageLog.isWarnMode()) {
						IgxLog.messageLog.warn(this,
								"Business record '{}' already existed in database.",
								toBeAddedBasicBusinessInfoBean.getBusinessName());
					}
					// ********************** Log **********************
				} catch (DataAccessException e2) {
					// ********************** Log **********************
					IgxLog.messageDetailsLog.error(this, e2.getMessage());
					// ********************** Log **********************

					throw e2;
				}
			}
			
			// (2) 新增業務目錄 
			try {
				// throws BusinessOperationException
				File createdBusinessFolder =
					this.businessOperation.createBatchBusinessTmpFolder(newBusinessName);

				// ********************** Log **********************
				if (IgxLog.messageLog.isDebugMode()) {
					IgxLog.messageLog.debug(this,
							"Created business folder '{}'.",
							createdBusinessFolder.getAbsolutePath());
				}
				// ********************** Log **********************
			} catch (BusinessOperationException e) {
				// ********************** Log **********************
				IgxLog.messageDetailsLog.error(this, e.getMessage());
				// ********************** Log **********************

				throw e;
			} finally {
				/*
				 *  從業務清單中新增業務。因為已經從 DB 將業務的記錄新增，所以無論
				 *  如何，清單也要新增(即便在目錄建立失敗的情況下)。
				 */

				super.basicBusinessInfoBeanMap.put(
						newBusinessName, 
						toBeAddedBasicBusinessInfoBean);
			}
		}
		// ********************** Log **********************
		IgxLog.messageLog.info(this,
				"Creating business '{}' operation completed.", newBusinessName);
		// ********************** Log **********************
	}
	
	@Override
	protected void synchronizeRemoveBusiness(
			Map<String, Object> businessFolderNameMap,
			Map<String, BasicBusinessInfoBean> basicBusinessInfoBeanInDbMap)
			throws BusinessManagementOperationException {
		
		// 如果業務目錄不存在於DB記錄中， 則必須刪除。

		Set<String> businessFolderNameSet = businessFolderNameMap.keySet();
		for (String businessFolderName : businessFolderNameSet) {
			if (!basicBusinessInfoBeanInDbMap.containsKey(businessFolderName)) {
				try {
					// 進行刪除
					
					// 要刪除"前"先通知 BusinessListener
					this.businessManagementListener.
							beforeSynchronizeRemovedBusinessNotify(businessFolderName);
					
					// throws DataAccessException, BusinessOperationException
					doSynchronizeRemoveBusinessAction(businessFolderName);
				} catch (DataAccessException e) {
					// 不會發生此種 Exception
				} catch (SynchronizeBusinessException e2) {
					try {
						throw new BusinessManagementOperationException(
								"SUDA-231 Removing business '" + businessFolderName +
								"' folder failed.", e2);
					} catch (BusinessManagementOperationException e) {
						// ********************** Log **********************
						IgxLog.messageLog.error(this, e.getMessage());
						// ********************** Log **********************

						throw e;
					}
				}
			}
		}
	}
	
	private void doSynchronizeRemoveBusinessAction(String toBeRemovedBusinessName)
													throws SynchronizeBusinessException {
		try {
			doRemoveBusiness(toBeRemovedBusinessName, false);
		} catch (DataAccessException e) {
			throw new SynchronizeBusinessException("" ,e);
		} catch (BusinessOperationException e) {
			throw new SynchronizeBusinessException("" ,e);
		}
	}
	
	private void doRemoveBusiness (String toBeRemovedBusinessNameStr, boolean isIncludeDbDelete)
	throws DataAccessException, BusinessOperationException {
		/*
		 * (1) 移除 DB (如果 isIncludeDbDelete 為 true)
		 * (2) 移除目錄
		 * (3) 從業務清單中移除業務
		 */

		synchronized (super.basicBusinessInfoBeanMap) {
			// 移除 DB 內的業務
			if (isIncludeDbDelete) {
				try {
					// throws DataAccessException
					int deletedCount =
						deleteBusinessFromDB(toBeRemovedBusinessNameStr);
					if (deletedCount <= 0) {
						// 代表資料庫裡原本就沒有該筆業務的存在

						// ********************** Log **********************
						if (IgxLog.messageLog.isWarnMode()) {
							IgxLog.messageLog.warn(this,
									"There is no business record '{}' in database that can be deleted.",
									toBeRemovedBusinessNameStr);
						}
						// ********************** Log **********************
					}
					else {
						// ********************** Log **********************
						if (IgxLog.messageLog.isDebugMode()) {
							IgxLog.messageLog.debug(this,
									"Deleted business record '{}' from database.", toBeRemovedBusinessNameStr);
						}
						// ********************** Log **********************
					}
				} catch (DataAccessException e) {
					// ********************** Log **********************
					IgxLog.messageDetailsLog.error(this, e.getMessage());
					// ********************** Log **********************

					throw e;
				}
			}

			// 移除目錄
			try {
				// throws BusinessOperationException
				File removedTerminalFolder =
					this.businessOperation.removeBatchBusinessTmpFolder(toBeRemovedBusinessNameStr);

				// ********************** Log **********************
				if (IgxLog.messageLog.isDebugMode()) {
					IgxLog.messageLog.debug(this,
							"Removed business folder '{}'.",
							removedTerminalFolder.getAbsolutePath());
				}
				// ********************** Log **********************
			} catch (BusinessOperationException e) {
				// ********************** Log **********************
				IgxLog.messageDetailsLog.error(this, e.getMessage());
				// ********************** Log **********************

				throw e;
			} finally {
				/*
				 *  從業務清單中移除業務。因為已經從 DB 將業務的記錄移除，所以無論
				 *  如何，都要從清單中移除(即便在目錄移除失敗的情況下)。
				 */
				super.basicBusinessInfoBeanMap.remove(toBeRemovedBusinessNameStr);
			}
		}

		// ********************** Log **********************
		IgxLog.messageLog.info(this,
				"Removing business '{}' operation completed.", toBeRemovedBusinessNameStr);
		// ********************** Log **********************
	}
	
	@Override
	public synchronized void addBusiness(String businessName, String businessType)
			throws BusinessManagementOperationException {
		// 檢查業務名稱和業務型態是否為null
		if (null == businessName || null == businessType) {
			try {
				throw new BusinessManagementOperationException(
						"SUDA-232 Adding business failed: didn't provide new business name or business type.");
			} catch (BusinessManagementOperationException e1) {
				// ********************** Log **********************
				IgxLog.messageLog.error(this, e1.getMessage());
				// ********************** Log **********************

				throw e1;
			}
		}
		
		// 檢查業務名稱長度是否正確
		if (businessName.length() != super.businessNameLen) {
			try {
				throw new BusinessManagementOperationException(
						"SUDA-233 Adding business failed: the length of business name '" +
						businessName + "' is incorrect.");
			} catch (BusinessManagementOperationException e1) {
				// ********************** Log **********************
				IgxLog.messageLog.error(this, e1.getMessage());
				// ********************** Log **********************

				throw e1;
			}
		}
		
		// 檢查業務型態別是否給定正確
		if (!HaishinBusinessUnitFactory.isEffectiveHaishinBusinessType(businessType)&&
			!SyushinBusinessUnitFactory.isEffectiveSyushinBusinessType(businessType)) {
			try {
				throw new BusinessManagementOperationException(
						"SUDA-234 Adding business failed: the business type '" +
						businessType + "' is incorrect.");
			} catch (BusinessManagementOperationException e1) {
				// ********************** Log **********************
				IgxLog.messageLog.error(this, e1.getMessage());
				// ********************** Log **********************

				throw e1;
			}
		}
		
		BasicBusinessInfoBean newBasicBusinessInfoBean =
							createBasicBusinessInfoBean(businessName,
														businessType);
		
		try {
			doAddBusiness(newBasicBusinessInfoBean, true);
			
			// 正確完成新增"後",通知 BusinessListener
			this.businessManagementListener.
					afterDynamicAddedBusinessNotify(newBasicBusinessInfoBean);
		} catch (DataAccessException e) {
			try {
				throw new BusinessManagementOperationException(
						"SUDA-235 Creating business '" + businessName +
						"' operation failed in database operation.",
						e);
			} catch (BusinessManagementOperationException e1) {
				// ********************** Log **********************
				IgxLog.messageLog.error(this, e1.getMessage());
				// ********************** Log **********************

				throw e1;
			}
		} catch (BusinessOperationException e) {
			try {
				throw new BusinessManagementOperationException(
						"SUDA-236 Creating business '" + businessName +
						"' operation failed in folder operation.",
						e);
			} catch (BusinessManagementOperationException e1) {
				// ********************** Log **********************
				IgxLog.messageLog.error(this, e1.getMessage());
				// ********************** Log **********************
				
				throw e1;
			}
		} 
	}

	@Override
	public synchronized void removeBusiness(String toBeRemovedBusinessName)
			throws BusinessManagementOperationException {
		// 檢查業務名稱是否為null
		if (null == toBeRemovedBusinessName) {
			try {
				throw new BusinessManagementOperationException(
						"SUDA-237 Removing business failed: didn't provide business name to be removed.");
			} catch (BusinessManagementOperationException e1) {
				// ********************** Log **********************
				IgxLog.messageLog.error(this, e1.getMessage());
				// ********************** Log **********************

				throw e1;
			}
		}
		
		try {
			// 要刪除"前"先通知 BusinessListener
			this.businessManagementListener.
					beforeDynamicRemovedBusinessNotify(toBeRemovedBusinessName);
			
			doRemoveBusiness(toBeRemovedBusinessName, true);
		} catch (DataAccessException e) {
			try {
				throw new BusinessManagementOperationException(
						"SUDA-238 Removing business '" + toBeRemovedBusinessName +
						"' operation failed in database operation.",
						e);
			} catch (BusinessManagementOperationException e1) {
				// ********************** Log **********************
				IgxLog.messageLog.error(this, e1.getMessage());
				// ********************** Log **********************
				
				throw e1;
			}
		} catch (BusinessOperationException e) {
			try {
				throw new BusinessManagementOperationException(
						"SUDA-321 Removing business '" + toBeRemovedBusinessName +
						"' operation failed in folder operation.",
						e);
			} catch (BusinessManagementOperationException e1) {
				// ********************** Log **********************
				IgxLog.messageLog.error(this, e1.getMessage());
				// ********************** Log **********************
				
				throw e1;
			}
		}
	}
	
	private void doRemoveBusiness (String toBeRemovedBusinessNameStr)
												throws DataAccessException {
		// 要刪除"前"先通知 TerminalListener
		this.businessManagementListener.
				beforeDynamicRemovedBusinessNotify(toBeRemovedBusinessNameStr);
		
		/*
		 * (1) 移除 DB
		 * (2) 從端末清單中移除業務
		 */
		
		synchronized (super.basicBusinessInfoBeanMap) {
			// (1) 移除 DB
			try {
				// throws DataAccessException
				int deletedCount =
					deleteBusinessFromDB(toBeRemovedBusinessNameStr);
				if (deletedCount <= 0) {
					// 代表資料庫裡原本就沒有該筆業務的存在
					
					// ********************** Log **********************
					if (IgxLog.messageLog.isWarnMode()) {
						IgxLog.messageLog.warn(this,
								"There is no business record '{}' in database that can be deleted.",
								toBeRemovedBusinessNameStr);
					}
					// ********************** Log **********************
				}
				else {
					// ********************** Log **********************
					if (IgxLog.messageLog.isDebugMode()) {
						IgxLog.messageLog.debug(this,
								"Deleted business record '{}' from database.",
								toBeRemovedBusinessNameStr);
					}
					// ********************** Log **********************
				}
			} catch (DataAccessException e) {
				// ********************** Log **********************
				IgxLog.messageDetailsLog.error(this, e.getMessage());
				// ********************** Log **********************
				
				throw e;
			}
			
			// (2) 從端末清單中移除業務
			super.basicBusinessInfoBeanMap.remove(toBeRemovedBusinessNameStr);
			
			// ********************** Log **********************
			IgxLog.messageLog.info(this,
					"Removing business '{}' operation completed.", toBeRemovedBusinessNameStr);
			// ********************** Log **********************
		}
	}
	
	private int deleteBusinessFromDB(String businessName) throws DataAccessException {
		return
			super.businessListMgtDao.deleteByBusinessName(businessName);
	}
	
	private void insertBusinessToDB(BasicBusinessInfoBean toBeAddedBasicBusinessInfoBean)
																throws DataAccessException {
		Map<String, Object> insertValuesMap = new HashMap<String, Object>();
		insertValuesMap.put(BusinessListMgtDao._businessName, toBeAddedBasicBusinessInfoBean.getBusinessName());
		insertValuesMap.put(BusinessListMgtDao._businessType, toBeAddedBasicBusinessInfoBean.getBusinessType());

		super.businessListMgtDao.insertBasic(insertValuesMap);
	}
	
	private BasicBusinessInfoBean createBasicBusinessInfoBean (
													String businessName,
													String businessType) {
		BasicBusinessInfoBean basicBusinessInfoBean = new BasicBusinessInfoBean();

		basicBusinessInfoBean.setBusinessName(businessName);
		basicBusinessInfoBean.setBusinessType(businessType);

		return basicBusinessInfoBean;
	}
	
	// --- getter and setter ---

	public void setBusinessOperation(BusinessOperation businessOperation) {
		this.businessOperation = businessOperation;
	}
	
}
